﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using MetroDropBox.Shared;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Diagnostics;
using Windows.Security.Authentication.Web;
using System.Net.Http;
using System.Net.Shared;

namespace MetroEventbrite.Shared
{
    public abstract class oAuth2ClientBase
    {
        protected OAuthToken AccessToken { get; set; }
        protected string _apiKey;
        protected string authHeaderRealm = "";

        protected bool passOAuthTokenByDefault = true;
        protected bool useAuthHeaderForHttpUris = false;
        protected bool useSha1SigningForHttpUris = false;
        protected bool useSha1Signing = true;
        protected bool usesAuthHeader = false;
        protected bool upperCaseUrlEncoding = false;
        protected bool usesOAuthVerifier = true;
        protected string getUserLoginURL;
        protected string authPageJsTweaks;

        public oAuth2ClientBase(string apiKey)
        {
            _apiKey = apiKey;
            init();

        }

        protected abstract void init();
        protected virtual void injectAdditionalParams(string url, Dictionary<string, string> parms) { }

        public virtual async Task<String> GetLoginUrlAsync(string redirectUrl)
        {
            return getUserLoginURL + _apiKey;
        }


        #region oAuth2 signing

        const string OAuthVersion = "2.0";
        
        #endregion

        #region HttpHelpers

        //
        // Custom UrlEncode
        //
        protected virtual String customUrlEncode(String ToEncode)
        {
            String Encoded = "";

            for (int Index = 0; Index < ToEncode.Length; Index++)
            {
                char Test = ToEncode[Index];
                if ((Test >= 'A' && Test <= 'Z') ||
                    (Test >= 'a' && Test <= 'z') ||
                    (Test >= '0' && Test <= '9'))
                {
                    Encoded += Test;
                }
                else if (Test == '-' || Test == '_' || Test == '.' || Test == '~')
                {
                    Encoded += Test;
                }
                else
                {
                    Encoded += "%" + String.Format("{0:X}", (int)Test);
                }
            }
            return Encoded;
        }
        private string urlEncode(string toEncode)
        {
            if (upperCaseUrlEncoding)
                return customUrlEncode(toEncode);
            else
                return WebUtility.UrlEncode(toEncode);
        }
        
        protected virtual async Task<string> httpPostForString(string url, Dictionary<string, string> parms, bool? passOAuthToken = null)
        {
            injectAppKey(parms);
            var qs = dictionaryToQSStringBuilder(parms).ToString();
            var authHeader = getAuthHeader();

            var postUrl = string.Format("{0}{1}{2}", url, qs.Length > 0 ? "?" : "", qs);
            string result = "";
#if DEBUG
            spitOutDebugInfo(url, qs, authHeader);
#endif
            try
            {
                result = await HttpRequestHelper.simpleHttpPost(postUrl, authHeader);
                Debug.WriteLine(String.Format("httpPostForString --> {0}", result));
                return result;
            }
            catch (WebException ex)
            {
            }
            return result;
        }

        private String dictionaryToQSStringBuilder(Dictionary<string, string> parms)
        {
            return String.Join("&", parms.Select(p => String.Format("{0}={1}", urlEncode(p.Key), urlEncode(p.Value))).ToArray());
        }
        protected virtual async Task<string> httpGetForString(string url, Dictionary<string, string> parms)
        {
            injectAppKey(parms);
            var qs = dictionaryToQSStringBuilder(parms).ToString();
            var authHeader = getAuthHeader();

            var getUrl = string.Format("{0}{1}{2}", url, qs.Length > 0 ? "?" : "", qs);
            string result = "";
#if DEBUG
            spitOutDebugInfo(url, qs, authHeader);
#endif
            try
            {
                result = await HttpRequestHelper.simpleHttpGet(getUrl, authHeader);

                Debug.WriteLine(String.Format("httpGetForString --> {0}", result));
                return result;
            }
            catch (WebException ex)
            {
            }
            return result;
        }

        private void injectAppKey(Dictionary<string, string> parms)
        {
            if (!parms.ContainsKey("app_key"))
            {
                parms.Add("app_key", _apiKey);
            }
        }

        private string getAuthHeader()
        {
            var authHeader = (AccessToken == null || String.IsNullOrEmpty(AccessToken.Token)) ? "" : String.Format("{0} {1}", AccessToken.TokenType, AccessToken.Token);
            return authHeader;
        }

        private void spitOutDebugInfo(string url, string qs, string authHeader)
        {
            Debug.WriteLine("baseUrl: " + url);
            if (qs.Length > 0)
            {
                Debug.WriteLine("?");
                var items = qs.Split('&');
                foreach (var item in items)
                    Debug.WriteLine("  " + item);
            }
            if (authHeader.Length > 0)
            {
                Debug.WriteLine("header: ");
                var items = authHeader.Split(',');
                foreach (var item in items)
                    Debug.WriteLine("  " + item);

            }
        }
        #endregion

        #region oAuth Signin
        public virtual async Task<Boolean> AuthorizeAsync(string redirectUrl)
        {
            String url = await GetLoginUrlAsync(redirectUrl);
            if (String.IsNullOrEmpty(url))
                return false;

            Debug.WriteLine(String.Format("WebAuthBroker --> {0}", url));

            var authResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(url), new Uri(redirectUrl));
            if (authResult.ResponseStatus == WebAuthenticationStatus.Success || authResult.ResponseStatus == WebAuthenticationStatus.UserCancel)
            {
                Debug.WriteLine(String.Format("WebAuthData: {0}", authResult.ResponseData));

                var uriData = authResult.ResponseData.ToString().Split(new [] {'?', '#'});
                var data = uriData.LastOrDefault();
                if (!String.IsNullOrEmpty(data))
                {
                    // get access token
                    AccessToken = new OAuthToken(data);
                    return true;
                }
            }
            return false;

            //var authToken = await GetAuthToken(redirectUrl);
            //return authToken != null;
        }        

        protected virtual OAuthToken parseAuthToken(string result)
        {
            return new OAuthToken(result);
        }

        #endregion
    }

}
